using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Linq;
using System.Web;
using Microsoft.Extensions.Logging;
using System.Text.Json.Serialization;
using DotPulsar;
using DotPulsar.Extensions;
using tafei_thingsapi.Services.auth;
using tafei_thingsapi.Models.Dtos;
using System.Buffers;

namespace tafei_thingsapi.Services
{
    public class OneNetService
    {
        private readonly HttpClient _httpClient;
        private readonly string _baseUrl;
        private readonly string _accessKey;
        private readonly string _user_id;
        private readonly string _product_id;
        private readonly string _consumer_key;
        private readonly ILogger<OneNetService> _logger;
        public readonly DotPulsar.Abstractions.IPulsarClient pulsarClient;

        public Dictionary<string, ThingPropertyMessage> msg_queue;

        public Dictionary<string, ThingPropertyMessage> MsgQueue
        {
            get{
                return msg_queue;
            }
        }

        public OneNetService(IConfiguration configuration, HttpClient httpClient, ILogger<OneNetService> logger)
        {
            _httpClient = httpClient;
            _accessKey = configuration["OneNet:AccessKey"];
            _user_id = configuration["OneNet:user_id"];
            _product_id = configuration["OneNet:product_id"];
            _baseUrl = configuration["OneNet:BaseUrl"];
            _consumer_key = configuration["OneNet:consumer_key"];

            _logger = logger;

            var _serviceUrl = configuration["Pulsar:ServiceUrl"];
            // _subscriptionName = configuration["Pulsar:SubscriptionName"];
            // _topic = configuration["Pulsar:Topic"];

            _httpClient.Timeout = TimeSpan.FromSeconds(100);


            pulsarClient = PulsarClient.Builder()
                .ServiceUrl(new Uri(_serviceUrl))
                .ConnectionSecurity(EncryptionPolicy.EnforceEncrypted)
                .VerifyCertificateName(true)
                .VerifyCertificateAuthority(false)
                .Authentication(new IoTAuthentication(configuration["OneNet:consumer_id"], _consumer_key))
                .RetryInterval(TimeSpan.FromSeconds(3))
                .Build();
            consume();
        }

        async void consume()
        {
            msg_queue = new Dictionary<string, ThingPropertyMessage>();
            var consumer = pulsarClient.NewConsumer(Schema.String)
            // .StateChangedHandler(PMonitor)
            .Topic("1pI6ZcwSyHVsQDYr6137/iot/event")
            .SubscriptionName("1pI6ZcwSyHVsQDYr6137-sub")
            .SubscriptionType(SubscriptionType.Failover)
            .Create();
            await foreach (var message in consumer.Messages())
            {
                string data = Encoding.UTF8.GetString(message.Data.ToArray());
                var obj = JsonSerializer.Deserialize<onenet_pulsr_message_body>(data);
                // Decrypt
                string decryptedMessage = Decrypt(obj.data);
                var msg = JsonSerializer.Deserialize<ThingPropertyMessage>(decryptedMessage);
                msg_queue[msg.subData.deviceName] = msg;
                _logger.LogInformation("收到消息: deviceName={} {decryptedMessage} ", msg.subData.deviceName,decryptedMessage);
                await consumer.AcknowledgeCumulative(message);
            }
        }

        public string Decrypt(string encryptedData)
        {
            return AESBase64Utils.Decrypt(encryptedData, _consumer_key.Substring(8, 16));
        }

        public static string AssembleToken(string version, string resourceName, string expirationTime, string signatureMethod, string accessKey)
        {
            var sb = new StringBuilder();
            string res = HttpUtility.UrlEncode(resourceName);
            string sig = HttpUtility.UrlEncode(GeneratorSignature(version, resourceName, expirationTime, accessKey, signatureMethod));

            sb.Append("version=")
              .Append(version)
              .Append("&res=")
              .Append(res)
              .Append("&et=")
              .Append(expirationTime)
              .Append("&method=")
              .Append(signatureMethod)
              .Append("&sign=")
              .Append(sig);

            return sb.ToString();
        }

        public static string GeneratorSignature(string version, string resourceName, string expirationTime, string accessKey, string signatureMethod)
        {
            string encryptText = expirationTime + "\n" + signatureMethod + "\n" + resourceName + "\n" + version;
            byte[] bytes = HmacEncrypt(encryptText, accessKey, signatureMethod);
            return Convert.ToBase64String(bytes);
        }

        public static byte[] HmacEncrypt(string data, string key, string signatureMethod)
        {
            byte[] keyBytes = Convert.FromBase64String(key);
            byte[] dataBytes = Encoding.UTF8.GetBytes(data);

            HMAC hmac = signatureMethod.ToUpper() switch
            {
                "SHA1" => new HMACSHA1(keyBytes),
                "MD5" => new HMACMD5(keyBytes),
                "SHA256" => new HMACSHA256(keyBytes),
                _ => throw new ArgumentException("Unsupported signature method")
            };

            return hmac.ComputeHash(dataBytes);
        }

        public enum SignatureMethod
        {
            SHA1, MD5, SHA256
        }

        private async Task<HttpResponseMessage> SendRequestAsync(HttpMethod method, string endpoint, object body = null)
        {
            var url = $"{_baseUrl}{endpoint}";
            var bodyString = body != null ? JsonSerializer.Serialize(body) : "";
            var signature = AssembleToken("2022-05-01", $"userid/{_user_id}", DateTimeOffset.UtcNow.AddMinutes(5).ToUnixTimeSeconds().ToString(), SignatureMethod.SHA256.ToString().ToLower(), _accessKey);

            _logger.LogInformation("发送请求: {Method} {Url}", method.Method, url);
            _logger.LogDebug("请求签名: {Signature}", signature);
            if (body != null)
            {
                _logger.LogDebug("请求体: {Body}", bodyString);
            }

            var request = new HttpRequestMessage(method, url);
            request.Headers.TryAddWithoutValidation("authorization", signature);

            if (body != null)
            {
                request.Content = new StringContent(
                    bodyString,
                    Encoding.UTF8,
                    "application/json"
                );
            }

            const int maxRetries = 3;
            for (int i = 0; i < maxRetries; i++)
            {
                try
                {
                    var response = await _httpClient.SendAsync(request);
                    var responseString = await response.Content.ReadAsStringAsync();

                    _logger.LogInformation("收到响应: {StatusCode}", response.StatusCode);
                    _logger.LogDebug("响应内容: {Response}", responseString);

                    if (!response.IsSuccessStatusCode)
                    {
                        _logger.LogError("请求失败: {StatusCode} {Response}", response.StatusCode, responseString);
                    }

                    return response;
                }
                catch (TaskCanceledException ex)
                {
                    if (i == maxRetries - 1)
                    {
                        _logger.LogError(ex, "请求在重试 {RetryCount} 次后仍然超时", maxRetries);
                        throw;
                    }
                    _logger.LogWarning("请求超时，正在进行第 {RetryCount} 次重试", i + 1);
                    await Task.Delay(1000 * (i + 1)); // 指数退避
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "请求发生错误");
                    throw;
                }
            }

            throw new TimeoutException($"请求在重试 {maxRetries} 次后仍然失败");
        }

        /// <summary>
        /// 创建设备
        /// https://open.iot.10086.cn/doc/v5/fuse/detail/1465
        /// </summary>
        public async Task<OneNetResponse<DeviceInfo>> CreateDeviceAsync(CreateDeviceRequest request)
        {
            request.product_id = _product_id;
            _logger.LogInformation("创建设备请求: {Request}", JsonSerializer.Serialize(request));
            var response = await SendRequestAsync(HttpMethod.Post, "/device/create", request);
            var responseString = await response.Content.ReadAsStringAsync();
            _logger.LogInformation("创建设备返回: {Response}", responseString);
            return JsonSerializer.Deserialize<OneNetResponse<DeviceInfo>>(responseString);
        }

        /// <summary>
        /// 获取设备详情
        /// </summary>
        public async Task<OneNetResponse<DeviceInfo>> GetDeviceAsync(string deviceId)
        {
            try
            {
                _logger.LogInformation("获取设备信息: {DeviceId}", deviceId);
                var response = await SendRequestAsync(HttpMethod.Get, $"/device/detail?product_id={_product_id}&device_name={deviceId}");
                var responseString = await response.Content.ReadAsStringAsync();
                _logger.LogInformation("获取设备信息返回: result={}", responseString);
                var result = JsonSerializer.Deserialize<OneNetResponse<DeviceInfo>>(responseString);
                if (result.code != 0)
                {
                    _logger.LogError("获取设备信息失败: {Code} {Msg}", result.code, result.msg);
                }

                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取设备信息时发生错误: {DeviceId} ex={ex}", deviceId, ex.Message);
                return null;
            }
        }

        /// <summary>
        /// 删除设备
        /// </summary>
        public async Task<OneNetResponse<object>> DeleteDeviceAsync(string deviceId)
        {
            var response = await SendRequestAsync(HttpMethod.Delete, $"/devices/{deviceId}");
            var responseString = await response.Content.ReadAsStringAsync();

            return JsonSerializer.Deserialize<OneNetResponse<object>>(responseString);
        }

        /// <summary>
        /// 获取设备属性详情
        /// </summary>
        public async Task<OneNetResponse<DeviceProperties>> GetDevicePropertiesdetailAsync(string deviceId, string[] properties=null)
        {
            try
            {
                _logger.LogInformation("获取设备属性详情: {DeviceId}, {Properties}", deviceId, properties==null?"":string.Join(",", properties));
                
                var requestBody = new
                {
                    product_id = _product_id,
                    device_name = deviceId,
                    @params = properties
                };

                var response = await SendRequestAsync(HttpMethod.Post, "/thingmodel/query-device-property-detail", requestBody);
                var responseString = await response.Content.ReadAsStringAsync();
                var result = JsonSerializer.Deserialize<OneNetResponse<DeviceProperties>>(responseString);
                _logger.LogInformation("获取设备属性详情返回: result={}", result);
                if (result.code != 0)
                {
                    _logger.LogError("获取设备属性详情失败: {Code} {Msg}", result.code, result.msg);
                }

                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取设备属性详情时发生错误: {DeviceId} ex={ex}", deviceId, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// 设置设备属性
        /// </summary>
        public async Task<OneNetResponse<object>> SetDevicePropertiesAsync(string deviceId, Dictionary<string, object> properties)
        {
            try
            {
                _logger.LogInformation("设置设备属性: {DeviceId}, {Properties}", deviceId, string.Join(",", properties.Keys));
                
                var requestBody = new
                {
                    product_id = _product_id,
                    device_name = deviceId,
                    @params = properties
                };

                var response = await SendRequestAsync(HttpMethod.Post, "/thingmodel/set-device-property", requestBody);
                var responseString = await response.Content.ReadAsStringAsync();
                var result = JsonSerializer.Deserialize<OneNetResponse<object>>(responseString);
                _logger.LogInformation("设置设备属性返回: result={}", result);
                if (result.code != 0)
                {
                    _logger.LogError("设置设备属性失败: {Code} {Msg}", result.code, result.msg);
                }

                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "设置设备属性时发生错误: {DeviceId} ex={ex}", deviceId, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// 调用设备服务
        /// </summary>
        public async Task<OneNetResponse<object>> InvokeDeviceServiceAsync(string deviceId, string serviceId, Dictionary<string, object> @params)
        {
            try
            {
                _logger.LogInformation("调用设备服务: {DeviceId}, {ServiceId}", deviceId, serviceId);
                
                var requestBody = new
                {
                    product_id = _product_id,
                    device_name = deviceId,
                    service_id = serviceId,
                    @params = @params
                };

                var response = await SendRequestAsync(HttpMethod.Post, "/thingmodel/invoke-service", requestBody);
                var responseString = await response.Content.ReadAsStringAsync();
                var result = JsonSerializer.Deserialize<OneNetResponse<object>>(responseString);
                _logger.LogInformation("调用设备服务返回: result={}", result);
                if (result.code != 0)
                {
                    _logger.LogError("调用设备服务失败: {Code} {Msg}", result.code, result.msg);
                }

                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "调用设备服务时发生错误: {DeviceId} ex={ex}", deviceId, ex.Message);
                throw;
            }
        }

        /// <summary>
        /// 获取设备属性最新数据
        /// </summary>
        public async Task<OneNetResponse<List<PropertyValue>>> GetLatestDevicePropertiesAsync(string deviceId)
        {
            try
            {
                _logger.LogInformation("获取设备属性最新数据: {DeviceId}", deviceId);
                
                var response = await SendRequestAsync(HttpMethod.Get, $"/thingmodel/query-device-property?product_id={_product_id}&device_name={deviceId}");
                var responseString = await response.Content.ReadAsStringAsync();
                _logger.LogInformation("获取设备属性最新数据返回: result={}", responseString);
                var result = JsonSerializer.Deserialize<OneNetResponse<List<PropertyValue>>>(responseString);
                _logger.LogInformation("获取设备属性最新数据返回: result={}", result);
                if (result.code != 0)
                {
                    _logger.LogError("获取设备属性最新数据失败: {Code} {Msg}", result.code, result.msg);
                }

                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取设备属性最新数据时发生错误: {DeviceId} ex={ex}", deviceId, ex.Message);
                return null;
            }
        }
    }

/*
{
    "product_id": "123",
    "device_name": "devname",
    "desc": "这是个设备描述",
    "lon": "123.45678",
    "lat": "123.45678",
    "imei": "123456789012345",
    "imsi": "123456789012345",
    "psk": "xxxxkkkk",
    "auth_code": "abcdefg",
    "tags": [
        "tag1",
        "tag2"
    ]
}*/
    public class CreateDeviceRequest
    {
        public string product_id { get; set; }
        public string device_name { get; set; }
        public string desc { get; set; }
        // public double lon { get; set; }
        // public double lat { get; set; }
        // public string imsi { get; set; }
        // public string imei { get; set; }
        // public string psk { get; set; }
        // public string auth_code { get; set; }
        // public string[] tags { get; set; }


    }

    public class LocationInfo
    {
        public double Lon { get; set; }
        public double Lat { get; set; }
        public double? Ele { get; set; }
    }

    public class AuthInfo
    {
        public string Username { get; set; }
        public string Password { get; set; }
    }

    public class DeviceInfo
    {
        public long did { get; set; }
        public string pid { get; set; }
        public int access_pt { get; set; }
        public string auth_info { get; set; }
        public int data_pt { get; set; }
        public string name { get; set; }
        public string desc { get; set; }
        //设备状态，0-离线，1-在线，2-未激活
        public int status { get; set; }
        public DateTime create_time { get; set; }
        public DateTime activate_time { get; set; }
        public DateTime last_time { get; set; }
        public string imsi { get; set; }
        public string imei { get; set; }
        public string psk { get; set; }
        public string group_id { get; set; }
        public bool enable_status { get; set; }
        public string[] tags { get; set; }
        public string lat { get; set; }
        public string lon { get; set; }
        public string auth_code { get; set; }
        public string sec_key { get; set; }
        public int chip { get; set; }
        public bool obsv { get; set; }
        public bool obsv_st { get; set; }

        [JsonPropertyName("private")]
        public bool private_zhuanyi { get; set; }
        public string[] imsi_old { get; set; }
        public DateTime imsi_mt { get; set; }
        public int intelligent_way { get; set; }

        public override string ToString()
        {
            return JsonSerializer.Serialize(this);
        }
    }

    public class OneNetResponse<T>
    {
        public int code { get; set; }
        public string msg { get; set; }
        public string request_id { get; set; }
        public T data { get; set; }
    }

    public class onenet_pulsr_message_body
    {
        public string data { get; set; }
        public string pv { get; set; }
        public string sign { get; set; }
        public int superMsg { get; set; }
        public long t { get; set; }
    }

    public class DeviceProperties
    {
        public Dictionary<string, PropertyValue> properties { get; set; }
        public string videoUrl { get; set; }
    }

    public class PropertyValue
    {
        public string access_mode { get; set; }
        public string data_type { get; set; }
        public string description { get; set; }
        public string expect_value { get; set; }
        public string identifier { get; set; }
        public string name { get; set; }
        public long time { get; set; }
        public string value { get; set; }
    }

    public class LatestDevicePropertiesResponse
    {
        public List<PropertyValue> list { get; set; }
    }
}